/*
 * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "display_common.h"
#include "securec.h"
#include "display_gfx.h"
#include <sys/mman.h>
#include <xf86drm.h>
#include "display_gfx.h"
#include "sprd_drm_gsp.h"
#include "gsp_cfg.h"
#include "gsp_r9p0_cfg.h"
#include "gsp_r9p0_common.h"

#include "v1_0/display_composer_type.h"
namespace OHOS {
namespace HDI {
namespace DISPLAY {
using namespace OHOS::HDI::Display::Composer::V1_0;
#define ALIGN_UP(x, a) ((((x) + ((a)-1)) / (a)) * (a))

#define GSP_DEVICE "/dev/dri/card0"
#define GSP_R9P0_NAME "R9P0"

static struct drm_gsp_r9p0_cfg_user *drm_mConfigs;
static struct gsp_r9p0_cfg_user *mConfigs;
static struct gsp_r9p0_img_layer_user mImgConfig[R9P0_IMGL_NUM];
static struct gsp_r9p0_osd_layer_user mOsdConfig[R9P0_OSDL_NUM];
static struct gsp_r9p0_des_layer_user mDstConfig;
static struct gsp_r9p0_misc_cfg_user mMiscConfig;

static int mDevice;
static int mCommitStatus = 0;

static void clearGspLayerInfo()
{
    for (int icnt = 0; icnt < R9P0_IMGL_NUM; icnt++) {
        memset(&mImgConfig[icnt], 0, sizeof(struct gsp_r9p0_img_layer_user));
    }
    for (int icnt = 0; icnt < R9P0_OSDL_NUM; icnt++) {
        memset(&mOsdConfig[icnt], 0, sizeof(struct gsp_r9p0_osd_layer_user));
    }
    memset(&mDstConfig, 0, sizeof(struct gsp_r9p0_des_layer_user));
    memset(&mMiscConfig, 0, sizeof(struct gsp_r9p0_misc_cfg_user));
    mInputRotMode = false;
    mCommitStatus = 1;
}

int32_t rkInitGfx()
{
    int fd;
    struct drm_gsp_capability drm_cap;

    fd = open(GSP_DEVICE, O_RDWR, S_IRWXU);

    if (fd < 0) {
      DISPLAY_LOGE("open gsp device failed fd=%d.", fd);
      return DISPLAY_FAILURE;
    }

    drm_cap.gsp_id = 0;
    drm_cap.size = sizeof(struct gsp_capability);
    drm_cap.cap = &mCapability.common;

    int ret = drmIoctl(fd, DRM_IOCTL_SPRD_GSP_GET_CAPABILITY, &drm_cap);

    if (ret < 0) {
      DISPLAY_LOGE("get gsp device capability failed ret=%d.", ret);
      close(fd);
      return DISPLAY_FAILURE;
    }

    if (mCapability.common.magic != GSP_CAPABILITY_MAGIC) {
      DISPLAY_LOGE("gsp device capability has not been initialized");
      close(fd);
      return DISPLAY_FAILURE;
    }

    DISPLAY_LOGI("gsp device version: %s, io count: %d, fd: %d", mCapability.common.version,
         mCapability.common.io_cnt, fd);

    //GspLiteR2P0PlaneArray::init

    if (mCapability.common.max_layer < 1) {
	  DISPLAY_LOGE("max layer params error");
	  return DISPLAY_FAILURE;
    }

    if(strcmp(GSP_R9P0_NAME, mCapability.common.version) == 0) {
        drm_cap.gsp_id = 0;
        drm_cap.size = sizeof(struct gsp_r9p0_capability);
        drm_cap.cap = &mCapability;
        drmIoctl(fd, DRM_IOCTL_SPRD_GSP_GET_CAPABILITY, &drm_cap);
        mConfigs = (struct gsp_r9p0_cfg_user *)malloc(sizeof(struct gsp_r9p0_cfg_user[mCapability.common.io_cnt]));

        drm_mConfigs =
        (struct drm_gsp_r9p0_cfg_user *)malloc(sizeof(struct drm_gsp_r9p0_cfg_user));
        drm_mConfigs->async = false;
        drm_mConfigs->config = mConfigs;
        drm_mConfigs->size = sizeof(struct gsp_r9p0_cfg_user);
        drm_mConfigs->num = 1;
        drm_mConfigs->split = false;
        drm_mConfigs->gsp_id = 0;
        mDevice = fd;
        memset(mConfigs, 0, sizeof(struct gsp_r9p0_cfg_user[mCapability.common.io_cnt]));
        clearGspLayerInfo();
        mCommitStatus = 0;
    } else {
        DISPLAY_LOGE("gsp version map fail! version: %{public}s", mCapability.common.version);
        close(fd);
        return false;
    }

    return DISPLAY_SUCCESS;
}

int32_t rkDeinitGfx()
{
    return DISPLAY_SUCCESS;
}

int32_t rkFillRect(ISurface *iSurface, IRect *rect, uint32_t color, GfxOpt *opt)
{
    DISPLAY_LOGE("%{public}s: ++", __func__);//没实现
    return DISPLAY_SUCCESS;
}

bool imgLayer_set(ISurface *srcSurface, IRect *srcRect, IRect *dstRect, GfxOpt *opt,struct gsp_r9p0_img_layer_params *params,struct gsp_layer_user *common)
{
    int inFBC = 0;
    //config rot
    if(common->enable) return false;
    enum gsp_rot_angle rot = rotationTypeConvert(opt->rotateType);
    if(rot) {
        mInputRotMode = true;//设置输入旋转
        // if(isLandScapeTransform(rot)) {
        //     int32_t tmp = dstRect->w;
        //     dstRect->w = dstRect->h;
        //     dstRect->h = tmp;
        // }
    }
    if(rot >= GSP_ROT_ANGLE_MAX_NUM) return false;
    //config Format configEndian
    params->img_format = convertImgFormat(srcSurface->enColorFmt, common, params, srcSurface->stride, srcSurface->height);

    //checkOddBoundary
    if(imgCheckOddBoundary(srcRect, params->img_format) == false) {
        if((srcRect->w & 0x01) || (srcRect->h & 0x01)) {
            srcRect->w = srcRect->w & ~0x01;
            srcRect->h = srcRect->h & ~0x01;
        }
        else {
            DISPLAY_LOGE("img do not support odd source layer xy.");
            return false;
        }
    }

    //checkRangeSize
    if(checkRangeSize(srcRect, dstRect) == false) return false;
    //AFBC 没有处理
    inFBC = 0;
    //处理缩放
    if(checkScale(srcRect, dstRect, rot, inFBC, -1) == false) {
        DISPLAY_LOGD("img do not support scale size.");
        return false;
    }
    //check blending
    //check InputRotation
    if((rot != GSP_ROT_ANGLE_0) && (checkInputRotation(srcRect, dstRect, rot, params->img_format, -1) == false)) {
        DISPLAY_LOGD("img do not support InputRotation.");
        return false;
    }
    //设置数据
    if(opt->blendType == BLEND_NONE &&
        GSP_R9P0_IMG_FMT_ARGB888 == params->img_format)
        params->img_format = GSP_R9P0_IMG_FMT_RGB888;
	//configSize
    params->clip_rect.st_x = srcRect->x;
    params->clip_rect.st_y = srcRect->y;
    params->clip_rect.rect_w = srcRect->w;
    params->clip_rect.rect_h = srcRect->h;

    params->des_rect.st_x = dstRect->x;
    params->des_rect.st_y = dstRect->y;
    params->des_rect.rect_w = dstRect->w;
    params->des_rect.rect_h = dstRect->h;

    params->pitch = srcSurface->stride / 4;
    params->height = srcSurface->height;
    if(isVideoLayerImg(params->img_format) == true && mCapability.yuv_xywh_even ==false) {
        if (needScale(srcRect, dstRect, rot) == true) {
            params->clip_rect.st_x &= 0xfffe;
            params->clip_rect.st_y &= 0xfffe;
            params->clip_rect.rect_w &= 0xfffe;
            params->clip_rect.rect_h &= 0xfffe;
        }
    }
    if(isVideoLayerImg(params->img_format)) {
        params->y2r_mod = 1;
        params->pitch = srcSurface->stride;
    }
    params->y2y_mod = 1;//禁止 yuv to yuv
    //混合模式
    if(opt->blendType == BLEND_SRCOVER || opt->blendType == BLEND_NONE)
        params->pmargb_mod = 1;
    else
        params->pmargb_mod = 0;
    params->pmargb_en = 0;
    //configFormat
    configScale(srcRect, dstRect, rot, params);
    //configZorder
    params->zorder = mCommitStatus++ -1;//opt->zorder;
    params->rot_angle = rot;
    params->secure_en = 0;
    params->pallet_en = 0;
    //configAlpha
    params->alpha = srcSurface->alpha0;
    //configCommon
    common->type = GSP_IMG_LAYER;
    common->enable = 1;
    common->wait_fd = -1;//src->GetAcquireFenceFd();//
    common->share_fd = srcSurface->phyAddr;
    return true;
}


bool osdLayer_set(ISurface *dstSource, IRect *srcRect, IRect *dstRect, GfxOpt *opt, struct gsp_r9p0_osd_layer_params *params,struct gsp_layer_user *common)
{
    // bool inFBC = 0;
    if(common->enable) return false;
    enum gsp_rot_angle rot = rotationTypeConvert(opt->rotateType);
    if(rot) return false;
    if(needScale(srcRect, dstRect, rot)) {
        DISPLAY_LOGD("osd do not support scale layer.");
        return false;
    }
    params->osd_format = osdFormatConvert(dstSource->enColorFmt, params, common);
    //check video
    if(isVideoLayerOsd(params->osd_format)) {
        DISPLAY_LOGD("osd do not support video layer.");
        return false;
    }
    //checkRangeSize
    if(checkRangeSize(srcRect, dstRect) == false) return false;
    //checkFBC
    //check blending
    //设置数据
    if(opt->blendType == BLEND_NONE &&
        GSP_R9P0_OSD_FMT_ARGB888 == params->osd_format)
        params->osd_format = GSP_R9P0_OSD_FMT_RGB888;
    //configure source clip size
    params->clip_rect.st_x = srcRect->x;
    params->clip_rect.st_y = srcRect->y;
    params->clip_rect.rect_w = srcRect->w;
    params->clip_rect.rect_h = srcRect->h;

    params->des_pos.pt_x = dstRect->x;
    params->des_pos.pt_y = dstRect->y;

    params->pitch = dstSource->stride / 4;
    params->height = dstSource->height;
    //混合模式
    if(opt->blendType == BLEND_SRCOVER || opt->blendType == BLEND_NONE)
        params->pmargb_mod = 1;
    else
        params->pmargb_mod = 0;
    params->pmargb_en = 0;
    params->alpha = dstSource->alpha0;
    params->zorder = mCommitStatus++ -1;//opt->zorder;
    params->pallet_en = 0;

    // DISPLAY_LOGD("gfx osd clip_rect={%{public}d,%{public}d:%{public}d,%{public}d}->des_pos={%{public}d,%{public}d} pitch=%{public}d height=%{public}d", 
    //             params->clip_rect.st_x, params->clip_rect.st_y, params->clip_rect.rect_w, params->clip_rect.rect_h, 
    //             params->des_pos.pt_x, params->des_pos.pt_y, params->pitch, params->height);

    common->type = GSP_OSD_LAYER;
    common->enable = 1;
    common->wait_fd = -1;//common->wait_fd = mAsync == true ? wait_fd : -1;
    common->share_fd = dstSource->phyAddr;
    return true;
}

bool dstLayer_set(int32_t fd, uint32_t stride, int32_t height, PixelFormat format, TransformType rotate, struct gsp_r9p0_des_layer_params *params, struct gsp_layer_user *common)
{
    if(common->enable) return false;
    params->rot_angle = rotationTypeConvert(rotate);
    //configCommon
    common->type = GSP_DES_LAYER;
    common->enable = 1;
    common->wait_fd = -1;//这个需要传参进来，晚点在处理
    common->share_fd = fd;
    //configPitch
    params->pitch = stride / 4;

    //configHeight
    params->height = height;

    //configFormat
    params->img_format = dstFormatConvert(format, params, common, stride / 4, height);

    struct gsp_background_para bg_para;
    bg_para.bk_enable = 1;
    bg_para.bk_blend_mod = 0;
    bg_para.background_rgb.a_val = 0;
    bg_para.background_rgb.r_val = 0;
    bg_para.background_rgb.g_val = 0;
    bg_para.background_rgb.b_val = 0;
    //configBackGround(bg_para);
    params->bk_para.bk_enable = bg_para.bk_enable;
    params->bk_para.bk_blend_mod = bg_para.bk_blend_mod;
    params->bk_para.background_rgb = bg_para.background_rgb;

    //configCSCMode
    params->r2y_mod = 0;

    //configDither
    // params->dither_en = 1;//抖动
    return true;
}

int32_t doSet(int w, int h)
{
    if(!mInputRotMode) {//输出旋转调整，暂未处理
        rotAdjust(&mConfigs[0], 0);
    }
    miscCfgParcel(&mMiscConfig, 0, mDstConfig.params.rot_angle, w, h);
    for(int icnt = 0; icnt < R9P0_IMGL_NUM; icnt++) {
        mConfigs[0].limg[icnt] = mImgConfig[icnt];
    }

    for(int icnt = 0; icnt < R9P0_OSDL_NUM; icnt++) {
        mConfigs[0].losd[icnt] = mOsdConfig[icnt];
    }

    mConfigs[0].ld1 = mDstConfig;
    mConfigs[0].misc = mMiscConfig;

    int ret = drmIoctl(mDevice, DRM_IOCTL_SPRD_GSP_TRIGGER, drm_mConfigs);
    if (ret < 0) {
        DISPLAY_LOGE("trigger gsp device failed ret=%d.", ret);
        return DISPLAY_FAILURE;
    } else {
        DISPLAY_LOGD("trigger gsp device success");
    }
    return ret;
}

int setToLayer(ISurface *srcSurface, IRect *srcRect, ISurface *dstSurface, IRect *dstRect, GfxOpt *opt)
{
    for (int icnt = 0; icnt < R9P0_OSDL_NUM; icnt++) {
        if(osdLayer_set(srcSurface, srcRect, dstRect, opt, &mOsdConfig[icnt].params, &mOsdConfig[icnt].common)) {
            return 0;
        }
    }
    for (int icnt = 0; icnt < R9P0_IMGL_NUM; icnt++) {
        if(imgLayer_set(srcSurface, srcRect, dstRect, opt, &mImgConfig[icnt].params, &mImgConfig[icnt].common)) {
            if((icnt + 1) == R9P0_IMGL_NUM) {
                return 1;
            } else {
                return 0;
            }
        }
    }
    return 2;
}

static bool imgIsFull()
{
    for(int32_t icnt = 0; icnt < R9P0_IMGL_NUM; icnt++) {
        if(!mImgConfig[icnt].common.enable) return false;
    }
    return true;
}

int32_t doFlit(ISurface *srcSurface, IRect *srcRect, ISurface *dstSurface, IRect *dstRect, GfxOpt *opt)
{
    if(mCommitStatus == 0) {
        clearGspLayerInfo();
        dstLayer_set(dstSurface->phyAddr, dstSurface->stride, dstSurface->height, dstSurface->enColorFmt, ROTATE_NONE, 
                    &mDstConfig.params, &mDstConfig.common);
    }

    sprdGfxOpt *sprdOpt =(sprdGfxOpt *)opt;
    int ret = setToLayer(srcSurface, srcRect, dstSurface, dstRect, sprdOpt->opt);
    if(ret || ((sprdOpt->index + 1) >= sprdOpt->maxCnt)) {
        doSet(mDstConfig.params.pitch, mDstConfig.params.height);
        mCommitStatus = 0;
        if((ret == 2) && imgIsFull()) {//还没有处理
            doFlit(srcSurface, srcRect, dstSurface, dstRect, opt);
        }
        else if((ret == 1) && ((sprdOpt->index + 1) < sprdOpt->maxCnt)) {
            sprdOpt->opt->rotateType = ROTATE_NONE;
            IRect rect = {0, 0, dstSurface->width, dstSurface->height};
            doFlit(dstSurface, &rect, dstSurface, &rect, opt);
        }
    }
    return DISPLAY_SUCCESS;
}

int32_t rkBlit(ISurface *srcSurface, IRect *srcRect, ISurface *dstSurface, IRect *dstRect, GfxOpt *opt)
{
    mLayerCount = 4;
    CHECK_NULLPOINTER_RETURN_VALUE(srcSurface, DISPLAY_NULL_PTR);
    CHECK_NULLPOINTER_RETURN_VALUE(srcRect, DISPLAY_NULL_PTR);
    CHECK_NULLPOINTER_RETURN_VALUE(dstSurface, DISPLAY_NULL_PTR);
    CHECK_NULLPOINTER_RETURN_VALUE(dstRect, DISPLAY_NULL_PTR);
    CHECK_NULLPOINTER_RETURN_VALUE(opt, DISPLAY_NULL_PTR);

    if (doFlit(srcSurface, srcRect, dstSurface, dstRect, opt) < 0)
        return DISPLAY_FAILURE;
    else
        return DISPLAY_SUCCESS;
}

int32_t rkSync(int32_t timeOut)
{
    return DISPLAY_SUCCESS;
}

extern "C" int32_t GfxInitialize(GfxFuncs **funcs)
{
    DISPLAY_CHK_RETURN((funcs == NULL), DISPLAY_PARAM_ERR, DISPLAY_LOGE("info is null"));
    GfxFuncs *gfxFuncs = (GfxFuncs *)malloc(sizeof(GfxFuncs));
    DISPLAY_CHK_RETURN((gfxFuncs == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("gfxFuncs is nullptr"));
    errno_t eok = memset_s((void *)gfxFuncs, sizeof(GfxFuncs), 0, sizeof(GfxFuncs));
    if (eok != EOK) {
        DISPLAY_LOGE("memset_s failed");
        free(gfxFuncs);
        return DISPLAY_FAILURE;
    }
    gfxFuncs->InitGfx = rkInitGfx;
    gfxFuncs->DeinitGfx = rkDeinitGfx;
    gfxFuncs->FillRect = rkFillRect;
    gfxFuncs->Blit = rkBlit;
    gfxFuncs->Sync = rkSync;
    *funcs = gfxFuncs;

    return DISPLAY_SUCCESS;
}

extern "C" int32_t GfxUninitialize(GfxFuncs *funcs)
{
    CHECK_NULLPOINTER_RETURN_VALUE(funcs, DISPLAY_NULL_PTR);
    free(funcs);
    DISPLAY_LOGI("%{public}s: gfx uninitialize success", __func__);
    return DISPLAY_SUCCESS;
}
} // namespace DISPLAY
} // namespace HDI
} // namespace OHOS